package utils

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/http/cookiejar"
	"reflect"
	"strconv"
	"strings"
	"time"
)

//lwyserver 接口服务
type Tools struct {
	Debug bool
}

//响应的数据格式
type Response struct {
	Code    uint32 `json:"Code"`    //错误码
	Version string `json:"Version"` //客户端版本号
	Msg     string `json:"Msg"`     //错误信息
	Data    []byte `json:"Data"`    //响应的ProtoBuf数据
}

//请求的数据格式
type Request struct {
	Code string //认证字符串
	Time string //请求的时间
	Data string //传输的数据：请求数据的json格式数据
}

//获取加密后的data数据
func (this *Tools) getAesDecryptData(key string, data interface{}) (string, error) {

	str, ok := AESDecrypt([]byte(key), data.(string))
	if !ok {
		return "", errors.New("数据解密失败")
	}

	return str, nil
}

func (this *Tools) GetRequest(requestDat []byte, ret interface{}) error {
	var req Request

	if err := json.Unmarshal(requestDat, &req); err != nil {
		return err
	}

	aesKey := this.getAesKey(req.Code, req.Time)

	decryptStr, err := this.getAesDecryptData(aesKey, req.Data)
	if nil != err {
		return err
	}

	err = json.Unmarshal([]byte(decryptStr), ret)
	if nil != err {
		return err
	}

	return nil
}

func (this *Tools) GetResponse(requestData interface{}) ([]byte, error) {
	request, err := this.makeRequest(requestData)
	if err != nil {
		return nil, err
	}

	response, err := json.Marshal(request)
	if err != nil {
		return nil, err
	}

	return response, nil
}

//获取数据
//url 请求地址
//requestData 请求数据
//response 响应数据
func (this *Tools) Post(url string, requestData interface{}, response *Response) error {

	//构造请求内容
	request, err := this.makeRequest(requestData)
	if err != nil {
		return err
	}
	bodyByte, err := this.ApiPost(url, request)
	if err != nil {
		return err
	}

	err = json.Unmarshal(bodyByte, response)
	if err != nil {
		log.Printf("Tools.Post错误,url:%s,body:%s\n", url, string(bodyByte))
		return err
	}

	return nil
}

//请求的数据格式
func (this *Tools) makeRequest(data interface{}) (Request, error) {

	var err error
	res := Request{
		Code: this.randomString(32),
		Time: strconv.Itoa(int(time.Now().Unix())),
	}

	aesKey := this.getAesKey(res.Code, res.Time)

	res.Data, err = this.getAesData(aesKey, data)
	if err != nil {
		return res, err
	}

	return res, nil

}

//生成随机字符串
func (this *Tools) randomString(num int) string {
	var letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, num)
	rand.Seed(time.Now().Unix())
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

//获取aes加密key
func (this *Tools) getAesKey(code string, time string) string {

	s := code + time
	strLen := len(s)

	evenStr := []byte{}

	for i := 0; i < strLen; i++ {
		if i%2 != 0 {
			evenStr = append(evenStr, []byte(s)[i])
		}
	}

	tmp := append(evenStr, []byte(s)...)

	return string(tmp[:16])
}

//获取加密后的data数据
func (this *Tools) getAesData(key string, data interface{}) (string, error) {

	if data == nil {
		return "", nil
	}

	jsonData, err := json.Marshal(data)

	if this.Debug {
		fmt.Println("请求数据:", string(jsonData))
	}
	if err != nil {
		return "", err
	}

	str, ok := AESEncrypt([]byte(key), string(jsonData))
	if !ok {
		return "", errors.New("数据加密失败")
	}

	return str, nil

}

/**
 * 提交POST的请求
 * url 请求地址
 * 请求参数
 */
func (this *Tools) ApiPost(url string, postParams Request) (bodyByte []byte, err error) {

	var (
		postStr   []byte
		cookieJar *cookiejar.Jar
		req       *http.Request
		resp      *http.Response
	)
	if this.Debug {
		fmt.Println(url)
	}

	if postStr, err = json.Marshal(postParams); err != nil {
		return
	}

	tr := &http.Transport{
		DisableKeepAlives: true,                                  //禁用长连接
		TLSClientConfig:   &tls.Config{InsecureSkipVerify: true}, //跳过证书验证
	}
	//http cookie接口
	if cookieJar, err = cookiejar.New(nil); err != nil {
		return
	}

	client := &http.Client{
		Jar:       cookieJar,
		Transport: tr,
		Timeout:   5 * time.Second,
	}

	if req, err = http.NewRequest("POST", url, strings.NewReader(string(postStr))); err != nil {
		return
	}

	req.Header.Set("Connection", "close")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	if resp, err = client.Do(req); err != nil {
		return
	}
	resp.Header.Set("Connection", "close")
	defer resp.Body.Close()

	if bodyByte, err = ioutil.ReadAll(resp.Body); err != nil {
		return
	}

	if this.Debug {
		fmt.Println("响应数据:", string(bodyByte))
	}

	return
}

//json，协议byte数据
func (this *Tools) PostUnmarshal(url string, req interface{}, ack interface{}) error {
	var (
		err      error
		response Response
	)

	if err = this.Post(url, req, &response); err != nil {
		return err
	}

	if response.Code != 0 {
		return errors.New(response.Msg)
	}

	if ack == nil {
		return nil
	}

	v := reflect.ValueOf(ack)
	if v.Kind() != reflect.Ptr {
		return errors.New("ack 必须为指针")
	}

	if err = json.Unmarshal(response.Data, ack); err != nil {
		return err
	}

	return nil
}

//json方式打印结构体
func JsonPrint(obj interface{}) {
	tmp, _ := json.MarshalIndent(obj, "", "     ")
	fmt.Println(string(tmp))
}
